package com.cnap.service.impl;

import com.cnap.CnapApplicationTests;
import com.cnap.db.entity.ClusterInfoPO;
import com.cnap.db.entity.MicroServicePO;
import com.cnap.db.entity.StagePO;
import com.cnap.db.entity.StrategyPO;
import com.cnap.db.mapper.StageMapper;
import com.cnap.db.mapper.StrategyMapper;
import com.cnap.model.cloud.CommentResultForCloud;
import com.cnap.model.cloud.CreateCloudParam;
import com.cnap.model.exception.JsonException;
import com.cnap.model.schedule.DistributeParam;
import com.cnap.model.schedule.MicroServiceVO;
import com.cnap.model.schedule.UpdateMicroServiceStatusParam;
import com.cnap.model.schedule.ValidateParam;
import com.cnap.model.schedule.engine.EngineClusterInfo;
import com.cnap.model.schedule.engine.alg.AlgDeployClusterInfo;
import com.cnap.model.schedule.engine.alg.ResourceTypeEnum;
import com.cnap.model.stage.SingleStageDeployParam;
import com.cnap.model.stage.StageDeployParam;
import com.cnap.model.stage.StageDetailTypeEnum;
import com.cnap.model.stage.StageTypeEnum;
import com.cnap.model.stage.StageVO;
import com.cnap.model.stage.UpdateStageParam;
import com.cnap.service.CloudMgmtService;
import com.cnap.service.ClusterInfoService;
import com.cnap.service.DistributeDeployService;
import com.cnap.service.MicroSvcService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <类文件描述>
 *
 * @author xc
 **/
class DistributeDeployServiceTest extends CnapApplicationTests {

    @Autowired
    private DistributeDeployService distributeDeployService;

    @MockBean
    private CloudMgmtService mockCloudMgmtService;
    @MockBean
    private MicroSvcService mockMicroSvcService;
    @MockBean
    private ClusterInfoService mockClusterInfoService;
    @MockBean
    private StageMapper stageMapper;
    @MockBean
    private StrategyMapper strategyMapperMock;


    @Test
    void startDistributeDeployWithNoMicroservice() {
        SingleStageDeployParam singleStageDeployParam = new SingleStageDeployParam();
        singleStageDeployParam.setSvcId("svcId");
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        singleStageDeployParam.setClusterInfos(Collections.singletonList(engineClusterInfo));
        singleStageDeployParam.setVms(Collections.emptyList());

        SingleStageDeployParam singleStageDeployParam1 = new SingleStageDeployParam();
        singleStageDeployParam1.setSvcId("svcId1");
        EngineClusterInfo engineClusterInfo22 = new EngineClusterInfo();
        singleStageDeployParam1.setClusterInfos(Collections.singletonList(engineClusterInfo22));
        singleStageDeployParam1.setVms(Collections.emptyList());

        StageDeployParam param = new StageDeployParam();
        param.setParams(Arrays.asList(singleStageDeployParam, singleStageDeployParam1));

        StrategyPO vmStrategy = new StrategyPO();
        Mockito.when(strategyMapperMock.selectVmScaleByServiceId("svcId1")).thenReturn(vmStrategy);

        MicroServicePO microServicePO = new MicroServicePO();
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId1")).thenReturn(microServicePO);

        EngineClusterInfo engineClusterInfo1 = new EngineClusterInfo();
        engineClusterInfo1.setName("name");
        List<EngineClusterInfo> list = new ArrayList<>();
        list.add(engineClusterInfo1);
        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        AlgDeployClusterInfo algDeployClusterInfo = new AlgDeployClusterInfo();
        algDeployClusterInfo.setProviderName("Huawei");
        algDeployClusterInfo.setResourceType(ResourceTypeEnum.CREATE.getType());
        clusterInfoPO.setAlgDeployClusterInfos(Collections.singletonList(algDeployClusterInfo));
        clusterInfoPO.setForcedIsolation(false);
        clusterInfoPO.setUserChooseCloud("Huawei");
        clusterInfoPO.setClusterInfos(list);

        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId1")).thenReturn(clusterInfoPO);

        Mockito.when(mockMicroSvcService.getYamlByServiceId("svcId")).thenReturn("");

        Assertions.assertThrows(JsonException.class, () -> {
            distributeDeployService.startDistributeDeploy(param);
        });
    }

    @Test
    void startDistributeDeploy() {
        SingleStageDeployParam singleStageDeployParam = new SingleStageDeployParam();
        singleStageDeployParam.setSvcId("svcId");
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        engineClusterInfo.setName("clusterName");
        engineClusterInfo.setProviderName("Huawei");
        singleStageDeployParam.setClusterInfos(Collections.singletonList(engineClusterInfo));
        singleStageDeployParam.setVms(Collections.emptyList());

        StageDeployParam param = new StageDeployParam();
        param.setParams(Collections.singletonList(singleStageDeployParam));

        MicroServicePO microServicePO = new MicroServicePO();
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);
        CommentResultForCloud result = new CommentResultForCloud();
        result.setCode(200);
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(result);
        CommentResultForCloud resultForCloud = new CommentResultForCloud();
        resultForCloud.setCode(200);
        Mockito.when(mockCloudMgmtService.insertNewScalingPolicy(Mockito.any())).thenReturn(resultForCloud);
        Mockito.when(mockMicroSvcService.updateMicroServiceStatus(Mockito.any(UpdateMicroServiceStatusParam.class))).thenReturn(true);

        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        AlgDeployClusterInfo algDeployClusterInfo = new AlgDeployClusterInfo();
        clusterInfoPO.setAlgDeployClusterInfos(Collections.singletonList(algDeployClusterInfo));
        clusterInfoPO.setClusterInfos(Collections.singletonList(engineClusterInfo));
        clusterInfoPO.setUserChooseCloud("Huawei");
        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId")).thenReturn(clusterInfoPO);


        StrategyPO vmStrategy = new StrategyPO();
        vmStrategy.setStrategyParam("{\"policy\":\"vm\",\"param\":[{\"id\":\"a7156a5eba084ff7b8753a9b0a38204a\",\"duration\":5,\"continuousNum\":1,\"coolTime\":88,\"type\":\"max\",\"num\":1,\"resource\":\"cpu\",\"value\":79,\"groupId\":\"63a5e2ca6e3e4d7184e440a4d01e2423\"},{\"id\":\"b7d3f6d3f2334a909692d3f4b65909b9\",\"duration\":5,\"continuousNum\":1,\"coolTime\":77,\"type\":\"min\",\"num\":1,\"resource\":\"cpu\",\"value\":77,\"groupId\":\"63a5e2ca6e3e4d7184e440a4d01e2423\"}]}");
        Mockito.when(strategyMapperMock.selectVmScaleByServiceId("svcId")).thenReturn(vmStrategy);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.startDistributeDeploy(param);
        });
    }

    @Test
    void startDistributeDeployWithSelfCluster() {
        SingleStageDeployParam singleStageDeployParam = new SingleStageDeployParam();
        singleStageDeployParam.setSvcId("svcId");
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        engineClusterInfo.setName("cluster");
        engineClusterInfo.setProviderName("Huawei");
        singleStageDeployParam.setClusterInfos(Collections.singletonList(engineClusterInfo));
        singleStageDeployParam.setVms(Collections.emptyList());

        StageDeployParam param = new StageDeployParam();
        param.setParams(Collections.singletonList(singleStageDeployParam));

        MicroServicePO microServicePO = new MicroServicePO();
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);
        CommentResultForCloud result = new CommentResultForCloud();
        result.setCode(200);
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(result);
        CommentResultForCloud resultForCloud = new CommentResultForCloud();
        resultForCloud.setCode(200);
        Mockito.when(mockCloudMgmtService.insertNewScalingPolicy(Mockito.any())).thenReturn(resultForCloud);
        Mockito.when(mockMicroSvcService.updateMicroServiceStatus(Mockito.any(UpdateMicroServiceStatusParam.class))).thenReturn(true);

        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        clusterInfoPO.setClusterInfos(Collections.singletonList(engineClusterInfo));
        clusterInfoPO.setAlgDeployClusterInfos(Collections.emptyList());
        clusterInfoPO.setUserChooseCloud("Huawei");
        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId")).thenReturn(clusterInfoPO);

        Mockito.when(mockMicroSvcService.validate(Mockito.any(ValidateParam.class))).thenReturn(true);
        Mockito.when(mockMicroSvcService.distribute(Mockito.any(DistributeParam.class))).thenReturn(true);


        StrategyPO vmStrategy = new StrategyPO();
        vmStrategy.setStrategyParam("{\"policy\":\"vm\",\"param\":[{\"id\":\"a7156a5eba084ff7b8753a9b0a38204a\",\"duration\":5,\"continuousNum\":1,\"coolTime\":88,\"type\":\"max\",\"num\":1,\"resource\":\"cpu\",\"value\":79,\"groupId\":\"63a5e2ca6e3e4d7184e440a4d01e2423\"},{\"id\":\"b7d3f6d3f2334a909692d3f4b65909b9\",\"duration\":5,\"continuousNum\":1,\"coolTime\":77,\"type\":\"min\",\"num\":1,\"resource\":\"cpu\",\"value\":77,\"groupId\":\"63a5e2ca6e3e4d7184e440a4d01e2423\"}]}");
        Mockito.when(strategyMapperMock.selectVmScaleByServiceId("svcId")).thenReturn(vmStrategy);


        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.startDistributeDeploy(param);
        });
    }

    @Test
    void getStageInfoByServiceId() {
        StagePO firstStagePo = new StagePO();
        firstStagePo.setType("cloud");
        firstStagePo.setId("idd");
        firstStagePo.setServiceId("svcId");
        firstStagePo.setError("");
        firstStagePo.setExtraInfo(Collections.singletonList(new HashMap<String, String>()));

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "cloud")).thenReturn(Collections.singletonList(firstStagePo));
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "port")).thenReturn(null);
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "k8s")).thenReturn(null);
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "distribute")).thenReturn(null);

        StageVO stageVO = distributeDeployService.getStageInfoByServiceId("svcId");

        Assertions.assertNotNull(stageVO);
    }

    @Test
    void getStagesInfoByAppId() {
        MicroServiceVO microServiceVO = new MicroServiceVO();
        microServiceVO.setId("svcId");

        Mockito.when(mockMicroSvcService.selectMicroServicesByAppId("appId")).thenReturn(Collections.singletonList(microServiceVO));

        StagePO firstStagePo = new StagePO();
        firstStagePo.setType("cloud");
        firstStagePo.setId("idd");
        firstStagePo.setServiceId("svcId");
        firstStagePo.setError("");
        firstStagePo.setExtraInfo(Collections.singletonList(new HashMap<String, String>()));

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "cloud")).thenReturn(Collections.singletonList(firstStagePo));
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "port")).thenReturn(null);
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "k8s")).thenReturn(null);
        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "distribute")).thenReturn(null);

        List<StageVO> stageVOs = distributeDeployService.getStagesInfoByAppId("appId");

        Assertions.assertNotNull(stageVOs);

    }

    @Test
    void retryByServiceIdWithCloudError() {
        StagePO errorStage = new StagePO();
        errorStage.setError("cloud error");

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "cloud")).thenReturn(Collections.singletonList(errorStage));
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(null);

        MicroServicePO microServicePO = new MicroServicePO();
        microServicePO.setCategory("ai");
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.retryByServiceId("svcId");
        });
    }

    @Test
    void retryByServiceIdWithPortError() {
        StagePO errorStage = new StagePO();
        errorStage.setError("port error");
        errorStage.setId("stageId");

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "port")).thenReturn(Collections.singletonList(errorStage));
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(null);
        Mockito.when(stageMapper.deleteStageById(errorStage.getId())).thenReturn(1);

        MicroServicePO microServicePO = new MicroServicePO();
        microServicePO.setCategory("ai");
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.retryByServiceId("svcId");
        });
    }

    @Test
    void retryByServiceIdWithK8sError() {
        StagePO errorStage = new StagePO();
        errorStage.setError("k8s error");
        errorStage.setId("stageId");

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "k8s")).thenReturn(Collections.singletonList(errorStage));
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(null);
        Mockito.when(stageMapper.deleteStageById(errorStage.getId())).thenReturn(1);

        MicroServicePO microServicePO = new MicroServicePO();
        microServicePO.setCategory("ai");
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.retryByServiceId("svcId");
        });
    }

    @Test
    void retryByServiceIdWithDistributeError() {
        StagePO errorStage = new StagePO();
        errorStage.setError("distribute error");

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "distribute")).thenReturn(Collections.singletonList(errorStage));
        Mockito.when(mockCloudMgmtService.createCloudResource(Mockito.any(CreateCloudParam.class))).thenReturn(null);

        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        engineClusterInfo.setName("name");
        engineClusterInfo.setProviderName("Huawei");
        clusterInfoPO.setClusterInfos(Collections.singletonList(engineClusterInfo));
        clusterInfoPO.setUserChooseCloud("Huawei");

        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId")).thenReturn(clusterInfoPO);
        Mockito.when(mockMicroSvcService.distribute(Mockito.any(DistributeParam.class))).thenReturn(true);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.retryByServiceId("svcId");
        });
    }

    @Test
    void updateStageWithoutPrevious() {
        UpdateStageParam param = new UpdateStageParam();
        param.setType("port");
        param.setSvcId("svcId");

        Mockito.when(stageMapper.selectStageByServiceIdAndType("svcId", "cloud")).thenReturn(null);

        boolean result = distributeDeployService.updateStage(param);

        Assertions.assertFalse(result);
    }

    @Test
    void updateStageWithCloud() {
        UpdateStageParam param = new UpdateStageParam();
        param.setType("cloud");
        param.setSvcId("svcId");
        param.setDetailType("1-1");
        param.setOrder(1);

        StagePO old = new StagePO();
        old.setId("oldId");

        Mockito.when(stageMapper.selectStageByServiceIdAndDetailType("svcId", "1-1")).thenReturn(old);
        Mockito.when(stageMapper.deleteStageById(old.getId())).thenReturn(1);
        boolean result = distributeDeployService.updateStage(param);

        Assertions.assertTrue(result);
    }

    @Test
    void updateStageWithK8s() {
        UpdateStageParam param = new UpdateStageParam();
        param.setType("k8s");
        param.setSvcId("svcId");
        param.setErrorMsg("");
        Map<String, String> extraInfo = new HashMap<>();
        extraInfo.put("ip", "1.1.1.1");
        extraInfo.put("innerIp", "1.1.10.1");
        extraInfo.put("name", "集群1");
        extraInfo.put("providerName", "Aliyun");
        Map<String, String> extraInfo1 = new HashMap<>();
        extraInfo1.put("ip", "1.1.1.2");
        extraInfo1.put("innerIp", "1.1.100.1");
        extraInfo1.put("name", "name");
        extraInfo1.put("providerName", "Aliyun");
        param.setExtraInfo(Arrays.asList(extraInfo, extraInfo1));
        param.setDetailType(StageDetailTypeEnum.THREE_SEVEN.getDetailType());

        StagePO old = new StagePO();
        old.setId("oldId");

        Mockito.when(stageMapper.selectStageByServiceIdAndDetailType("svcId",
                StageDetailTypeEnum.THREE_SIX.getDetailType())).thenReturn(old);
        Mockito.when(stageMapper.deleteStageById(old.getId())).thenReturn(1);

        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        engineClusterInfo.setName("name");
        engineClusterInfo.setProviderName("Huawei");
        List<EngineClusterInfo> list = new ArrayList<>();
        list.add(engineClusterInfo);
        clusterInfoPO.setClusterInfos(list);
        clusterInfoPO.setUserChooseCloud("Huawei");
        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId")).thenReturn(clusterInfoPO);

        MicroServicePO microServicePO = new MicroServicePO();
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);
        Mockito.doNothing().when(mockClusterInfoService).saveOrUpdateClusterInfo(microServicePO, clusterInfoPO.getClusterInfos());
        Mockito.when(mockMicroSvcService.validate(Mockito.any(ValidateParam.class))).thenReturn(true);
        Mockito.when(mockMicroSvcService.distribute(Mockito.any(DistributeParam.class))).thenReturn(true);


        boolean result = distributeDeployService.updateStage(param);

        Assertions.assertTrue(result);
    }

    @Test
    void updateStageWithExp() {
        UpdateStageParam param = new UpdateStageParam();
        param.setType("k8s");
        param.setSvcId("svcId");
        param.setErrorMsg("");
        Map<String, String> extraInfo = new HashMap<>();
        extraInfo.put("ip", "1.1.1.1");
        extraInfo.put("innerIp", "1.1.10.1");
        extraInfo.put("name", "集群1");
        extraInfo.put("providerName", "Aliyun");
        Map<String, String> extraInfo1 = new HashMap<>();
        extraInfo1.put("ip", "1.1.1.2");
        extraInfo1.put("innerIp", "1.1.100.1");
        extraInfo1.put("name", "name");
        extraInfo1.put("providerName", "Aliyun");
        param.setExtraInfo(Arrays.asList(extraInfo, extraInfo1));
        param.setDetailType(StageDetailTypeEnum.FOUR_ONE.getDetailType());

        StagePO old = new StagePO();
        old.setId("oldId");

        Mockito.when(stageMapper.selectStageByServiceIdAndDetailType("svcId",
                StageDetailTypeEnum.THREE_SEVEN.getDetailType())).thenReturn(old);
        Mockito.when(stageMapper.deleteStageById(old.getId())).thenReturn(1);

        ClusterInfoPO clusterInfoPO = new ClusterInfoPO();
        EngineClusterInfo engineClusterInfo = new EngineClusterInfo();
        engineClusterInfo.setName("name");
        List<EngineClusterInfo> list = new ArrayList<>();
        list.add(engineClusterInfo);
        clusterInfoPO.setClusterInfos(list);
        Mockito.when(mockClusterInfoService.getClusterInfoBySvcId("svcId")).thenReturn(clusterInfoPO);

        MicroServicePO microServicePO = new MicroServicePO();
        Mockito.when(mockMicroSvcService.getMicroserviceBySvcId("svcId")).thenReturn(microServicePO);
        Mockito.doNothing().when(mockClusterInfoService).saveOrUpdateClusterInfo(microServicePO, clusterInfoPO.getClusterInfos());
        Mockito.when(mockMicroSvcService.validate(Mockito.any(ValidateParam.class))).thenThrow(new JsonException(500,
                "exp"));
        Mockito.when(mockMicroSvcService.distribute(Mockito.any(DistributeParam.class))).thenReturn(true);


        boolean result = distributeDeployService.updateStage(param);

        Assertions.assertTrue(result);
    }

    @Test
    void testDeleteStagesByServiceId() {
        Mockito.when(stageMapper.deleteStageByServiceId("svcId")).thenReturn(1);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.deleteStagesByServiceId("svcId");
        });
    }

    @Test
    void testUpdateExtraInfo() {
        Mockito.when(stageMapper.updateStageExtraInfoBySvcIdAndDetailType(Mockito.any(), Mockito.any(),
                Mockito.any())).thenReturn(1);

        Assertions.assertDoesNotThrow(() -> {
            distributeDeployService.updateExtraInfo("svcId", StageDetailTypeEnum.THREE_SEVEN, null);
        });
    }

    @Test
    void testDeleteStagesBySvcIdAndType() {
        String svcId = "svcId";
        Mockito.when(stageMapper.deleteStageByServiceIdAndType(svcId, StageTypeEnum.DISTRIBUTE.getType())).thenReturn(1);
        Assertions.assertDoesNotThrow(()->{
            distributeDeployService.deleteStagesBySvcIdAndType(svcId, StageTypeEnum.DISTRIBUTE.getType());
        });

    }
}